using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using GPSCore.NMEA.COMSource;
using GPSCore.NMEA.Interfaces;
using GPSCore.NMEA.FileSource;

namespace GPSCoreExample
{
    public partial class GPSCoreTest : Form
    {
        /// <summary>
        /// Form 
        /// </summary>
        public GPSCoreTest()
        {
            InitializeComponent();


            initCOMPortTab();

            
            //GPSCore.Position p1 = new GPSCore.Position(new GPSCore.Latitude(44, 25.336), new GPSCore.Longitude(9, 0.663), 110.0);
            //GPSCore.Position p2 = new GPSCore.Position(new GPSCore.Latitude(44, 25.651), new GPSCore.Longitude(9, 0.529), 260.0);

            

            //GPSCore.Distance d_p1_p2 = new GPSCore.Distance(p1, p2);

            ////GPSCore.Distance di = new GPSCore.Distance( 100.0 , new GPSCore.Azimuth(0.0));

            ////GPSCore.Position newp = p1 + di;

            ////GPSCore.Distance d2_p1_p2 = new GPSCore.Distance(p1, newp);


            //GPSCore.Distance d_p2_p1 = new GPSCore.Distance(p2, p1);
            

            


        }


        private void initCOMPortTab()
        {
            cmbCOMPorts.Items.Clear();
            cmbCOMPorts.Items.AddRange(GPSCore.NMEA.COMSource.COMPortThreaded.GetPortNames);   
        }


        private TimeSpan performanceTest()
        {
            DateTime initialDt = DateTime.Now;
            lstbPerformanceResults.Items.Add("Initial test time: " + initialDt.ToString("dd/MM/yyyy - HH:mm:ss.ffff"));

            int numberOfLoop = 1000000;
            lstbPerformanceResults.Items.Add("Creating " + numberOfLoop + " Angle objects");

            for (int testLoop = 0; testLoop < numberOfLoop; testLoop++)
            {
                //GPSCore.Angle angle = new GPSCore.Angle(300.0);
                //angle.Min = -90.0;
                //angle.Max = 90.0;
                //angle.Normalize();

                GPSCore.Position p1 = new GPSCore.Position(new GPSCore.Latitude(44, 25.336), new GPSCore.Longitude(9, 0.663), 110.0);

            }

            DateTime endDt = DateTime.Now;
            lstbPerformanceResults.Items.Add("End test time: " + endDt.ToString("dd/MM/yyyy - HH:mm:ss.ffff") + " Elapsed secods: " + ((TimeSpan)(endDt - initialDt)).TotalMilliseconds / 1000.0);

            return endDt - initialDt;     
        }

        
        private void button1_Click(object sender, EventArgs e)
        {
            int loopExecutionNumbers = 10;

            double timeSum = 0;

            for (int testLoop = 0; testLoop < loopExecutionNumbers; testLoop++)
            {
                timeSum += performanceTest().TotalMilliseconds;
            }

            lstbPerformanceResults.Items.Add("Loop " + loopExecutionNumbers + " times. Avarage Execution Time: " + ( (timeSum / loopExecutionNumbers) / 1000.0));
        }



        private GPSCore.GPX.GPXManager _gpxmanager;

        private void btnOpenGPXFile_Click(object sender, EventArgs e)
        {
            openFileDialog.DefaultExt = "gpx";
            openFileDialog.FileOk += new CancelEventHandler(btnOpenFileHandler);
            openFileDialog.ShowDialog();
        }
        
        private void btnOpenFileHandler(object o, CancelEventArgs cea)
        {
            lblOpenGPXFile.Text = ((OpenFileDialog)o).FileName;
            txbGPXContent.Text = String.Empty;

            if (((OpenFileDialog)o).FileName.ToLower().Contains(".gpx"))
            {
                try
                {
                    _gpxmanager = new GPSCore.GPX.GPXManager(((OpenFileDialog)o).FileName);
                }
                catch (Exception ex)
                {
                    txbGPXContent.Text = ex.Message;
                }

                if (_gpxmanager != null)
                {
                    txbGPXContent.Text = "Creator: " +  _gpxmanager.RawGpxData.creator + "\n";
                    txbGPXContent.Text += "Waypoints: " + (_gpxmanager.RawGpxData.wpt != null ? _gpxmanager.RawGpxData.wpt.Length.ToString() : "No Waypoints") + "\n";
                    txbGPXContent.Text += "Traks: " + (_gpxmanager.RawGpxData.trk != null ? _gpxmanager.RawGpxData.trk.Length.ToString() : "No Traks") + "\n";
                    txbGPXContent.Text += "Rte: " + (_gpxmanager.RawGpxData.rte != null ? _gpxmanager.RawGpxData.rte.Length.ToString() : "No Rte") + "\n";
                }               
            }
            else
            {
                txbGPXContent.Text = "";
                lblOpenGPXFile.Text = "File Not Valid";
            }
        }



        #region generic NMEA Source code

        /// <summary>
        /// this is a NMEA Source. 
        /// 
        /// This will be setted or to a COM Port or a TextFile.
        /// </summary>
        private GPSCore.NMEA.Interfaces.INMEASource m_NMEASource;

        private delegate void nmeaForcedStop_AsyncDelegate();
        private delegate void receiveSentenceLine_AsyncDelegate(string data);

        private void receiveSentenceLine(string data)
        {
            if (lsbNMEASourceRawData.InvokeRequired)
            {
                receiveSentenceLine_AsyncDelegate d = new receiveSentenceLine_AsyncDelegate(receiveSentenceLine);
                this.Invoke(d, new object[] { data });
            }
            else
            {
                lsbNMEASourceRawData.Items.Add(data);
            }
        }
        
        #endregion

        #region COM Port NMEASource 

        /// <summary>
        /// this is a COM Port NMEA Source
        /// </summary>
        private GPSCore.NMEA.COMSource.COMPortThreaded m_COMPort;


        private void btnOpenComPort_Click(object sender, EventArgs e)
        {
            if (_comConfiguration != null)
            {
                _comConfiguration.PortName = cmbCOMPorts.Text;
                _comConfiguration.BaudRate = Convert.ToInt32(cmbCOMSpeed.Text);
            }

            m_COMPort = new GPSCore.NMEA.COMSource.COMPortThreaded(_comConfiguration);

            setupNMEASourceAndEngine(m_COMPort);

            m_NMEASource.OnNMEAForceStop += new GPSCore.NMEA.Interfaces.NMEAForceStop(nmeaCOMForcedStop);

            btnOpenComPort.Enabled = false;
            btnCloseComPort.Enabled = true;

            btnCloseNMEAFile.Enabled = false;
            btnOpenNMEAFile.Enabled = false;
        }

        private void btnCloseComPort_Click(object sender, EventArgs e)
        {
            if (m_NMEASource != null)
            {
                m_NMEASource.Stop();
                m_NMEASource = null;

                btnCloseComPort.Enabled = false;

                btnOpenComPort.Enabled = true;
                btnOpenNMEAFile.Enabled = true;
            }
        }    

        private void nmeaCOMForcedStop()
        {
            if (btnCloseComPort.InvokeRequired | btnOpenComPort.InvokeRequired)
            {
                nmeaForcedStop_AsyncDelegate d = new nmeaForcedStop_AsyncDelegate(nmeaCOMForcedStop);
                this.Invoke(d, new object[0]);
            }
            else
            {
                btnCloseComPort.Enabled = false;
                btnOpenComPort.Enabled = true;

                btnOpenNMEAFile.Enabled = true;
            }
        }

        COMLocator _comLocator = new COMLocator();

        private void btnSearchGPS_Click(object sender, EventArgs e)
        {
            _comLocator.OnGPSFoundOnComPort += GPSFounded;
            _comLocator.OnGPSNotFoundOnComPort += GPSNotFounded;

            btnSearchGPS.Enabled = false;

            _comLocator.Start();
        }

        COMConfiguration _comConfiguration;

        private void GPSFounded(COMConfiguration configuration)
        {
            _comConfiguration = configuration;
            setGPSComPort(configuration);
        }

        private void GPSNotFounded()
        {
            setGPSComPort(null);
        }

        delegate void setGPSComPort_AsyncDelegate(COMConfiguration configuration);

        private void setGPSComPort(COMConfiguration configuration)
        {
            if (cmbCOMPorts.InvokeRequired | cmbCOMSpeed.InvokeRequired)
            {
                setGPSComPort_AsyncDelegate d = new setGPSComPort_AsyncDelegate(setGPSComPort);
                this.Invoke(d, new object[] { configuration });
            }
            else
            {
                if (configuration != null)
                {
                    cmbCOMPorts.Text = configuration.PortName;
                    cmbCOMSpeed.Text = configuration.BaudRate.ToString();
                }
                else
                {
                    cmbCOMPorts.Text = "unknown";
                    cmbCOMSpeed.Text = "unknown";
                }

                btnSearchGPS.Enabled = true;
            }
        }


        
        #endregion


        /// <summary>
        /// initialize the NMEA source and engine
        /// </summary>
        /// <param name="source"></param>
        private void setupNMEASourceAndEngine(INMEASource source)
        {
            m_NMEASource = source;

            //handle the raw nmea sentence receiver...
            m_NMEASource.OnNMEASentenceRecieved += new GPSCore.NMEA.Interfaces.NMEASentenceRecieved(receiveSentenceLine);
            
            //create a nmea engine based on a specific nmea source!
            _nmeaEngine = new GPSCore.NMEA.NMEAEngine(m_NMEASource);

            _nmeaEngine.OnPositionUpdate += new GPSCore.NMEA.PositionUpdateEventHandler(positionUpdateHandler);
            _nmeaEngine.OnSatalliteInfoUpdate += new GPSCore.NMEA.SatalliteInfoEventHandler(satUpdateHandler);

            //start the engine
            _nmeaEngine.Start();        
        }


        #region open test nmea file

        private GPSCore.NMEA.FileSource.NMEAFileReader m_FileReader;
        

        private void btnOpenNMEAFile_Click(object sender, EventArgs e)
        {
            openFileDialog.DefaultExt = "nmea";
            openFileDialog.FileOk += new CancelEventHandler(btnOpenNMEAFileHandler);
            openFileDialog.ShowDialog();
        }



        private void btnOpenNMEAFileHandler(object o, CancelEventArgs cea)
        {
            lblNMEAFileName.Text = ((OpenFileDialog)o).FileName;

            if (((OpenFileDialog)o).FileName.ToLower().Contains(".nmea"))
            {
                m_FileReader = new NMEAFileReader(lblNMEAFileName.Text);

                setupNMEASourceAndEngine(m_FileReader);

                m_NMEASource.OnNMEAForceStop += new GPSCore.NMEA.Interfaces.NMEAForceStop(nmeaFileForcedStop);

                btnOpenComPort.Enabled = false;
                btnCloseComPort.Enabled = false;

                btnCloseNMEAFile.Enabled = true;
                btnOpenNMEAFile.Enabled = false;
            }
            else
            {
                lblNMEAFileName.Text = "File Not Valid";
            }
        }


        private void nmeaFileForcedStop()
        {
            if (btnCloseComPort.InvokeRequired | btnOpenComPort.InvokeRequired)
            {
                nmeaForcedStop_AsyncDelegate d = new nmeaForcedStop_AsyncDelegate(nmeaFileForcedStop);
                this.Invoke(d, new object[0]);
            }
            else
            {
                btnCloseNMEAFile.Enabled = false;
                btnOpenNMEAFile.Enabled = true;
                btnOpenComPort.Enabled = true;
            }
        }


        private void btnCloseNMEAFile_Click(object sender, EventArgs e)
        {
            if (m_NMEASource != null)
            {
                m_NMEASource.Stop();
                m_NMEASource = null;

                btnCloseNMEAFile.Enabled = false;

                btnOpenComPort.Enabled = true;
                btnOpenNMEAFile.Enabled = true;
            }
        }



        #endregion



        /// <summary>
        /// The NMEA Engine
        /// </summary>
        GPSCore.NMEA.NMEAEngine _nmeaEngine;


        delegate void positionUpdateHandler_AsyncDelegate(GPSCore.NMEA.NMEAEngine engine);

        /// <summary>
        /// this method will be called every time a new position is obtained!
        /// </summary>
        /// <param name="engine"></param>
        private void positionUpdateHandler(GPSCore.NMEA.NMEAEngine engine)
        {
            //update the last position indicator!
            if ((lblLastLong.InvokeRequired) | (lblLastLat.InvokeRequired) | (lblLastElevation.InvokeRequired) | ( lblLastTimeStamp.InvokeRequired) )
            {
                positionUpdateHandler_AsyncDelegate d = new positionUpdateHandler_AsyncDelegate(positionUpdateHandler);
                this.Invoke(d, new object[] { engine });
            }
            else
            {
                this.lblLastLat.Text = engine.Position.Latitude.ToString();
                this.lblLastLong.Text = engine.Position.Longitude.ToString();
                this.lblLastElevation.Text = engine.Position.Altitude.HasValue ? engine.Position.Altitude.Value.ToString() : "n/d";
                this.lblLastTimeStamp.Text = engine.LastFixTimeStamp.HasValue ? engine.LastFixTimeStamp.Value.ToLocalTime().ToString() : "n/d";
            }    
        }




        delegate void satUpdateHandler_AsyncDelegate(GPSCore.NMEA.NMEAEngine engine);

        private void satUpdateHandler(GPSCore.NMEA.NMEAEngine engine)
        {
            if (lblNumberOfSatinView.InvokeRequired)
            {
                satUpdateHandler_AsyncDelegate d = new satUpdateHandler_AsyncDelegate(satUpdateHandler);
                this.Invoke(d, new object[] { engine });
            }
            else
            {
                //set the satellite information
                lblNumberOfSatinView.Text = engine.SatelliteCollection.Count.ToString();

            }        
        }


    }
}